<template>
  <div v-transfer-dom :data-transfer="transfer">
    <transition name="fade">
      <div v-if="mask && visible" :class="maskClasses" :style="maskStyle" @click="handleMask" />
    </transition>
    <div :class="wrapClasses" @click="handleWrapClick">
      <transition :name="'move-' + placement">
        <div v-if="visible" :class="classes" :style="mainStyles">
          <div ref="content" :class="contentClasses">
            <a v-if="closable" class="ivu-drawer-close" @click="close">
              <slot name="close">
                <icon-svg icon-class="close" />
              </slot>
            </a>
            <div v-if="showHead" :class="[prefixCls + '-header']"><slot name="header"><div :class="[prefixCls + '-header-inner']">{{ title }}</div></slot></div>
            <div :class="[prefixCls + '-body']" :style="styles"><slot /></div>
          </div>
          <div v-if="draggable" class="ivu-drawer-drag" :class="{ 'ivu-drawer-drag-left': placement === 'left' }" @mousedown="handleTriggerMousedown">
            <slot name="trigger">
              <div class="ivu-drawer-drag-move-trigger">
                <div class="ivu-drawer-drag-move-trigger-point">
                  <i /><i /><i /><i /><i />
                </div>
              </div>
            </slot>
          </div>
        </div>
      </transition>
    </div>
  </div>
</template>
<script>
import { oneOf, findBrothersComponents, findComponentsUpward } from '../utils/assist'
import Emitter from '../mixins/emitter'
import ScrollbarMixins from '../mixins/scrollbar-mixins'
import { on, off } from '../utils/dom'
const prefixCls = 'ivu-drawer'

export default {
  name: 'Drawer',
  mixins: [Emitter, ScrollbarMixins],
  props: {
    value: {
      type: Boolean,
      default: false
    },
    title: {
      type: String
    },
    width: {
      type: [Number, String],
      default: 256
    },
    closable: {
      type: Boolean,
      default: true
    },
    maskClosable: {
      type: Boolean,
      default: true
    },
    mask: {
      type: Boolean,
      default: true
    },
    maskStyle: {
      type: Object
    },
    styles: {
      type: Object
    },
    scrollable: {
      type: Boolean,
      default: false
    },
    placement: {
      validator(value) {
        return oneOf(value, ['left', 'right'])
      },
      default: 'right'
    },
    zIndex: {
      type: Number,
      default: 1000
    },
    transfer: {
      type: Boolean,
      default() {
        return true
      }
    },
    className: {
      type: String,
      default: ''
    },
    inner: {
      type: Boolean,
      default: false
    },
    // Whether drag and drop is allowed to adjust width
    draggable: {
      type: Boolean,
      default: false
    },
    beforeClose: {
      type: Function,
      default: () => {}
    }
  },
  data() {
    return {
      prefixCls,
      visible: this.value,
      wrapShow: false,
      showHead: true,
      canMove: false,
      dragWidth: this.width,
      wrapperWidth: this.width,
      wrapperLeft: 0,
      minWidth: 256
    }
  },
  computed: {
    wrapClasses() {
      return [
        `${prefixCls}-wrap`,
        {
          [`${prefixCls}-hidden`]: !this.wrapShow,
          [`${this.className}`]: !!this.className,
          [`${prefixCls}-no-mask`]: !this.mask,
          [`${prefixCls}-wrap-inner`]: this.inner,
          [`${prefixCls}-wrap-dragging`]: this.canMove
        }
      ]
    },
    mainStyles() {
      const style = {}

      const width = parseInt(this.dragWidth)

      const styleWidth = {
        width: width <= 100 ? `${width}%` : `${width}px`
      }

      Object.assign(style, styleWidth)

      return style
    },
    contentClasses() {
      return [
        `${prefixCls}-content`,
        {
          [`${prefixCls}-content-no-mask`]: !this.mask
        }
      ]
    },
    classes() {
      return [
        `${prefixCls}`,
        `${prefixCls}-${this.placement}`,
        {
          [`${prefixCls}-no-header`]: !this.showHead,
          [`${prefixCls}-inner`]: this.inner
        }
      ]
    },
    maskClasses() {
      return [
        `${prefixCls}-mask`,
        {
          [`${prefixCls}-mask-inner`]: this.inner
        }
      ]
    }
  },
  watch: {
    value(val) {
      this.visible = val
    },
    visible(val) {
      if (val === false) {
        this.timer = setTimeout(() => {
          this.wrapShow = false
          // #4831 Check if there are any drawers left at the parent level
          const brotherDrawers = findBrothersComponents(this, 'Drawer') || []
          const parentDrawers = findComponentsUpward(this, 'Drawer') || []

          const otherDrawers = [].concat(brotherDrawers).concat(parentDrawers)

          const isScrollDrawer = otherDrawers.some((item) => item.visible && !item.scrollable)

          if (!isScrollDrawer) {
            this.removeScrollEffect()
          }
        }, 300)
      } else {
        if (this.timer) clearTimeout(this.timer)
        this.wrapShow = true
        if (!this.scrollable) {
          this.addScrollEffect()
        }
      }
      this.broadcast('Table', 'on-visible-change', val)
      this.broadcast('Slider', 'on-visible-change', val) // #2852
      this.$emit('on-visible-change', val)
    },
    scrollable(val) {
      if (!val) {
        this.addScrollEffect()
      } else {
        this.removeScrollEffect()
      }
    },
    title(val) {
      if (this.$slots.header === undefined) {
        this.showHead = !!val
      }
    },
    width(val) {
      this.dragWidth = val
    }
  },
  mounted() {
    if (this.visible) {
      this.wrapShow = true
    }

    let showHead = true

    if (this.$slots.header === undefined && !this.title) {
      showHead = false
    }

    this.showHead = showHead

    on(document, 'mousemove', this.handleMousemove)
    on(document, 'mouseup', this.handleMouseup)
    this.handleSetWrapperWidth()
  },
  beforeDestroy() {
    off(document, 'mousemove', this.handleMousemove)
    off(document, 'mouseup', this.handleMouseup)
    this.removeScrollEffect()
  },
  methods: {
    close() {
      if (!this.beforeClose) {
        return this.handleClose()
      }

      const before = this.beforeClose()

      if (before && before.then) {
        before.then(() => {
          this.handleClose()
        })
      } else {
        this.handleClose()
      }
    },
    handleClose() {
      this.visible = false
      this.$emit('input', false)
      this.$emit('on-close')
    },
    handleMask() {
      if (this.maskClosable && this.mask) {
        this.close()
      }
    },
    handleWrapClick(event) {
      // use indexOf,do not use === ,because ivu-modal-wrap can have other custom className
      const className = event.target.getAttribute('class')
      if (className && className.indexOf(`${prefixCls}-wrap`) > -1) this.handleMask()
    },
    handleMousemove(event) {
      if (!this.canMove || !this.draggable) return
      // 更新容器宽度和距离左侧页面距离，如果是window则距左侧距离为0
      this.handleSetWrapperWidth()
      const left = event.pageX - this.wrapperLeft
      // 如果抽屉方向为右边，宽度计算需用容器宽度减去left
      let width = this.placement === 'right' ? this.wrapperWidth - left : left
      // 限定最小宽度
      width = Math.max(width, parseFloat(this.minWidth))
      event.atMin = width === parseFloat(this.minWidth)
      // 如果当前width不大于100，视为百分比
      if (width <= 100) width = (width / this.wrapperWidth) * 100
      this.dragWidth = width
      this.$emit('on-resize-width', parseInt(this.dragWidth))
    },
    handleSetWrapperWidth() {
      const {
        width,
        left
      } = this.$el.getBoundingClientRect()
      this.wrapperWidth = width
      this.wrapperLeft = left
    },
    handleMouseup() {
      if (!this.draggable) return
      this.canMove = false
    },
    handleTriggerMousedown() {
      this.canMove = true
      // 防止鼠标选中抽屉中文字，造成拖动trigger触发浏览器原生拖动行为
      window.getSelection().removeAllRanges()
    }
  }
}
</script>
<style lang="less">
@drawer-prefix-cls: ~"ivu-drawer";
@border-color-split     : #e8eaec;
@font-size-base         : 14px;
@title-color            : #17233d;
@font-size-small        : 12px;
.mask() {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    background-color: rgba(55, 55, 55, 0.6);
    height: 100%;
    z-index: 1000;

    &-hidden {
        display: none;
    }
}
.content-header() {
    border-bottom: 1px solid @border-color-split;
    padding: 14px 16px;
    line-height: 1;

    p,
    &-inner
    {
        display: inline-block;
        width: 100%;
        height: 20px;
        line-height: 20px;
        font-size: @font-size-base;
        color: @title-color;
        font-weight: bold;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
    p i, p span{
        vertical-align: middle;
    }
}
.close-base(@top: 0, @icon-font-size: 22px) {
    font-size: @icon-font-size;
    color: @legend-color;
    transition: color @transition-time ease;
    position: relative;
    top: @top;
    &:hover {
        color: #444;
    }
}
.content-close(@top: 0, @icon-font-size: 22px) {
    font-size: @font-size-small;
    position: absolute;
    right: 8px;
    top: 8px;
    overflow: hidden;
    cursor: pointer;

    
}
.@{drawer-prefix-cls} {
    width: auto;
    height: 100%;
    position: fixed;
    top: 0;

    &-inner{
        position: absolute;
    }

    &-left{
        left: 0;
    }
    &-right{
        right: 0;
    }

    &-hidden {
        display: none !important;
    }

    &-wrap {
        position: fixed;
        overflow: auto;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
        z-index: 1000;
        -webkit-overflow-scrolling: touch;
        outline: 0;

        &-inner{
            position: absolute;
            overflow: hidden;
        }

        &-dragging{
            user-select: none;
        }
    }

    &-wrap * {
        box-sizing: border-box;
        -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
    }

    &-mask {
        .mask;
        &-inner{
            position: absolute;
        }
    }

    &-content {
        width: 100%;
        height: 100%;
        position: absolute;
        top: 0;
        bottom: 0;
        background-color: #fff;
        border: 0;
        background-clip: padding-box;
        box-shadow: 0 4px 12px rgba(0,0,0,.15);

        &-no-mask{
            pointer-events: auto;
        }
    }

    &-header {
        .content-header;
    }

    &-close {
        z-index: 1;
        .content-close(1px, 31px);
    }

    &-body {
        width: 100%;
        height: calc(~'100% - 51px');
        padding: 16px;
        font-size: 12px;
        line-height: 1.5;
        word-wrap: break-word;
        position: absolute;
        overflow: auto;
    }

    &-no-header &-body{
        height: 100%;
    }

    &-no-mask{
        pointer-events: none;

        .@{drawer-prefix-cls}-drag{
            pointer-events: auto;
        }
    }

    &-drag{
        top: 0;
        height: 100%;
        width: 0;
        position: absolute;
        &-left{
            right: 0;
        }
        &-move-trigger{
            width: 8px;
            height: 100px;
            line-height: 100px;
            position: absolute;
            top: 50%;
            background: rgb(243, 243, 243);
            transform: translate(-50%, -50%);
            border-radius: ~"4px / 6px";
            box-shadow: 0 0 1px 1px rgba(0, 0, 0, .2);
            cursor: col-resize;
            &-point{
                display: inline-block;
                width: 50%;
                transform: translateX(50%);
                i{
                    display: block;
                    border-bottom: 1px solid rgb(192, 192, 192);
                    padding-bottom: 2px;
                }
            }
        }
    }
}
</style>
<style>
.ivu-drawer-mask {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    background-color: rgba(55,55,55,.6);
    height: 100%;
    z-index: 1000;
}
.ivu-drawer-wrap {
    position: fixed;
    overflow: auto;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 1000;
    -webkit-overflow-scrolling: touch;
    outline: 0;
}
.ivu-drawer-wrap * {
    box-sizing: border-box;
    -webkit-tap-highlight-color: transparent;
}
.ivu-drawer-right {
    right: 0;
}
.ivu-drawer {
    width: auto;
    height: 100%;
    position: fixed;
    top: 0;
}
.ivu-drawer-content {
    width: 100%;
    height: 100%;
    position: absolute;
    top: 0;
    bottom: 0;
    background-color: #fff;
    border: 0;
    background-clip: padding-box;
    box-shadow: 0 4px 12px rgb(0 0 0 / 15%);
}
.ivu-drawer-header {
    border-bottom: 1px solid #e8eaec;
    padding: 14px 16px;
    line-height: 1;
}
.ivu-drawer-body {
    width: 100%;
    height: calc(100% - 51px);
    padding: 16px;
    font-size: 12px;
    line-height: 1.5;
    word-wrap: break-word;
    position: absolute;
    overflow: auto;
}
.ivu-drawer-hidden {
    display: none;
}
.ivu-drawer-close {
    z-index: 1;
    font-size: 12px;
    position: absolute;
    right: 8px;
    top: 8px;
    overflow: hidden;
    cursor: pointer;
}

</style>
